﻿using IntoTheVoid.Models;
using Microsoft.VisualBasic.FileIO;
using Microsoft.Win32;
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Security.Cryptography;
using System.Windows.Forms;
using System.Windows.Shapes;
using System.Xaml;
using System.Xml.Linq;
using Path = System.IO.Path;
using Rectangle = System.Drawing.Rectangle;

namespace IntoTheVoid.Units
{
	public static class Common
	{
		[DllImport("CrashSight64.dll")]
		static extern void CS_InitContext(string userId, string version, string key);

		[DllImport("CrashSight64.dll")]
		static extern void CS_ReportException(int type, string name, string message, string stackTrace, string extras);

		[DllImport("CrashSight64.dll")]
		private static extern void CS_ReportExceptionW(int type, [MarshalAs(UnmanagedType.LPStr)] string name, [MarshalAs(UnmanagedType.LPStr)] string message, [MarshalAs(UnmanagedType.LPStr)] string stack_trace,
								  [MarshalAs(UnmanagedType.LPStr)] string extras, bool is_async, [MarshalAs(UnmanagedType.LPWStr)] string attachmentPath = "");

		[DllImport("CrashSight64.dll", CallingConvention = CallingConvention.Cdecl)]
		private static extern void CS_SetCustomLogDirW([MarshalAs(UnmanagedType.LPWStr)] string log_path);

		public static void InitializeCrash()
		{
			var VersionString = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();
			// 设置崩溃上报
			CS_InitContext("userId", VersionString, "0e7f1ad7-2b60-4e1b-a051-c08ed9c289bb");
			var dat = System.DateTime.Now.ToString("yyyy-MM-dd");
			String path = Environment.CurrentDirectory + "/Logs/" + dat + ".txt";
			if (File.Exists(path))
				CS_SetCustomLogDirW(path);
		}
		public static void ReportException(string name, string message, Exception e)
		{
			var dat = System.DateTime.Now.ToString("yyyy-MM-dd");
			String path = Environment.CurrentDirectory + "/Logs/" + dat + ".txt";
			CS_ReportExceptionW(1, "启动器" + name, message, e.StackTrace, e.Message, true, path);
		}
		private static List<string> picList = new List<string>();

		public static void DeleteReportFile()
		{
			foreach(var pic in picList)
			{
				try
				{
					File.Delete(pic);
				}
				catch (Exception e)
				{
					LogHelper.Error(e);
				}
			}
			picList.Clear();
		}

		public static void ReportScreenshot(string message, string extra = "")
		{
			try
			{
				var screenshotPath = CaptureAndSaveScreenshot();
				if (screenshotPath != null && File.Exists(screenshotPath))
				{
					ReportPicture(screenshotPath, message, extra);
					picList.Add(screenshotPath);
				}
				else
				{
					CS_ReportException(1, "启动器" + "cheating program report", "check out cheatprogram without pic", "trace", "extra");
				}
			}
			catch (Exception e)
			{
				LogHelper.Error(e);
			}

		}
		/// <summary>
		/// 
		/// </summary>
		/// <param name="path"></param>
		/// <param name="message"></param>
		/// <param name="extra"></param>
		public static void ReportPicture(string path, string message = "", string extra = "")
		{
			string errorName = "cheating program report";
			CS_ReportExceptionW(1, "启动器" + errorName, message, extra, extra, true, path);
		}
		public static string GenerateRandomString(int length = 16)
		{
			const string validChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
			char[] chars = new char[length];
			Random random = new Random();
			for (int i = 0; i < length; i++)
			{
				chars[i] = validChars[random.Next(validChars.Length)];
			}
			return new string(chars);
		}
		public static string CaptureAndSaveScreenshot()
		{
			Rectangle screenBounds = Screen.PrimaryScreen.Bounds;
			using (Bitmap screenshot = new Bitmap(screenBounds.Width, screenBounds.Height))
			{
				using (Graphics g = Graphics.FromImage(screenshot))
				{
					g.CopyFromScreen(screenBounds.Location, Point.Empty, screenBounds.Size);
				}

				string tempDirectory = Path.GetTempPath();
				var dat = GenerateRandomString() + ".tmp";
				string filePath = Path.Combine(tempDirectory, dat);

				SaveCompressedImage(screenshot, filePath);

				return filePath;
			}
		}

		private static void SaveCompressedImage(Bitmap image, string filePath)
		{
			long maxSize = 5 * 1024 * 1024; // 5MB
			long fileSize;

			using (MemoryStream memoryStream = new MemoryStream())
			{
				SaveImageAsJpegWithQuality(image, memoryStream, 90); 
				fileSize = memoryStream.Length;

				while (fileSize > maxSize && memoryStream.Length > 0)
				{
					memoryStream.SetLength(0);  
					SaveImageAsJpegWithQuality(image, memoryStream, 75);
					fileSize = memoryStream.Length;
				}
				File.WriteAllBytes(filePath, memoryStream.ToArray());
			}
		}

		private static void SaveImageAsJpegWithQuality(Bitmap image, MemoryStream memoryStream, int quality)
		{
			var encoder = GetJpegEncoder();
			var parameters = new EncoderParameters(1);
			parameters.Param[0] = new EncoderParameter(Encoder.Quality, quality);
			image.Save(memoryStream, encoder, parameters);
		}

		private static ImageCodecInfo GetJpegEncoder()
		{
			foreach (ImageCodecInfo codec in ImageCodecInfo.GetImageEncoders())
			{
				if (codec.FormatID == ImageFormat.Jpeg.Guid)
				{
					return codec;
				}
			}
			return null;
		}


		public static string FormatByteSizeNum(long num)
		{
			if (num <= 0)
				return "0";
			double Dnum = (double)num;
			List<string> list = new List<string> { "B", "KB", "MB", "GB", "TB" };
			int i = 0;
			while ((Dnum > 1024) && (i < 9 - 1))
			{
				Dnum /= 1024;
				i += 1;
			}
			if (Dnum == Double.NaN)
			{
				Dnum = 0;
			}
			return string.Format("{0:F2}", Dnum) + list[i];
		}
		public static string FormatByteNum(long num)
		{
			if (num <= 0)
				return "0.00KB/S";
			double Dnum = (double)num;
			List<string> list = new List<string> { "B", "KB", "MB", "GB", "TB" };
			int i = 0;
			while ((Dnum > 1024) && (i < 9 - 1))
			{
				Dnum /= 1024;
				i += 1;
			}
			if (Dnum == Double.NaN)
			{
				Dnum = 0;
			}
			return string.Format("{0:F2}", Dnum) + list[i];
		}

		public static string FormatByteNum(double num)
		{
			if (num == double.PositiveInfinity || num == double.NegativeInfinity || num == double.NaN || num == double.Epsilon)
				return "1.0MB";

			List<string> list = new List<string> { "B", "KB", "MB", "GB", "TB" };
			int i = 0;
			while ((num > 1024) && (i < 9 - 1))
			{
				num /= 1024;
				i += 1;
			}
			return string.Format("{0:F2}", num) + list[i];
		}


		public static string FormatTime(long time)
		{
			long m = time / 60;
			long s = time % 60;
			long h = m / 60;
			m %= 60;

			return string.Format("{0:D2}:{1:D2}:{2:D2}", h, m, s);
		}


		public static string GetFileNameFromUrl(string url)
		{
			try
			{
				// 使用 Uri 类来解析 URL
				Uri uri = new Uri(url);

				// 使用 Path.GetFileName 方法来获取文件名
				string fileName = System.IO.Path.GetFileName(uri.LocalPath);
				return fileName;
			}
			catch (Exception ex)
			{
				Console.WriteLine("Error: " + ex.Message);
				return null;
			}
		}


		public static string GetTempFileName(string fileName)
		{
			return System.IO.Path.GetFileNameWithoutExtension(fileName) + ".zip_tmp"; ;
		}


		public static bool CheckFileExists(string targetPath, string fileName = "")
		{
			try
			{
				string filePath = System.IO.Path.Combine(targetPath, fileName);
				return File.Exists(filePath);
			}
			catch (Exception ex)
			{
				Console.WriteLine("Error: " + ex.Message);
				LogHelper.Error(ex, "File Not Exist:" + System.IO.Path.Combine(targetPath, fileName));
				return false;
			}
		}

		public static string ChangeFileName(string filePath)
		{
			var name = System.IO.Path.GetFileNameWithoutExtension(filePath);
			var directoryPath = System.IO.Path.GetDirectoryName(filePath);

			if (File.Exists(filePath))
			{
				string newName = name + ".zip";
				FileSystem.RenameFile(filePath, newName);
				filePath = System.IO.Path.Combine(directoryPath, newName);
			}
			return filePath;
		}
		public static long GetHardDiskFreeSpace(string hardDiskName)
		{
			long freeSpace = new long();
			if (!hardDiskName.EndsWith("\\"))
			{
				hardDiskName = hardDiskName + ":\\";
			}
			DriveInfo[] drives = DriveInfo.GetDrives();
			foreach (DriveInfo drive in drives)
			{
				if (drive.Name == hardDiskName)
				{
					freeSpace = drive.TotalFreeSpace;
					break;
				}
			}
			return freeSpace;
		}
		public static void OpenLauncherUpdate(string url)
		{
			string path = Directory.GetCurrentDirectory();
			DirectoryInfo pathInfo = new DirectoryInfo(path);
			var updateDir = System.IO.Path.Combine(path, "LauncherUpdate");
			var launcherPath = System.IO.Path.Combine(updateDir, "LauncherUpdate.exe");
			if (File.Exists(launcherPath))
			{
				Process externalProcess = new Process();
				externalProcess.StartInfo.FileName = launcherPath;
				externalProcess.EnableRaisingEvents = true;
				externalProcess.StartInfo.WorkingDirectory = updateDir;
				// externalProcess.StartInfo.Arguments = "http://package.jinzhangshu.com/SecondTest/IntoTheVoidSetup_1.0.0.258.exe";
				externalProcess.StartInfo.Arguments = url;
				externalProcess.Start();
				Environment.Exit(0);
			}
		}
		/// <summary>
		/// 检测本地文件是否存在
		/// </summary>
		/// <param name="version"></param>
		public static bool CheckGameFolderIsInstalled(string version)
		{
			// 检查json文件和exe文件 传入线上Version
			bool result = false;
			var defaultPath = System.IO.Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Game");
			var targetPath = BaseInfoContentModel.Instance.GetValue<string>("gameInstallPath", defaultPath);
			if (!targetPath.EndsWith("IntoTheVoid"))
			{
				targetPath = System.IO.Path.Combine(targetPath, "IntoTheVoid");
			}
			var filename = version + "_info.json";

			var fileInfoJson = System.IO.Path.Combine(targetPath, filename);
			var gameFilePath = System.IO.Path.Combine(targetPath, "IntoTheVoid.exe");
			if (File.Exists(fileInfoJson) && File.Exists(gameFilePath))
			{
				BaseInfoContentModel.Instance.SetValue("fileInfoJson", fileInfoJson);
				BaseInfoContentModel.Instance.SetValue("version", version);
				result = true;
			}
			
			// 如果在当前路径没找到，再深入一层IntoTheVoid文件夹检查
			if (!result)
			{
				var deeperPath = System.IO.Path.Combine(targetPath, "IntoTheVoid");
				if (Directory.Exists(deeperPath))
				{
					var deeperFileInfoJson = System.IO.Path.Combine(deeperPath, filename);
					var deeperGameFilePath = System.IO.Path.Combine(deeperPath, "IntoTheVoid.exe");
					
					if (File.Exists(deeperFileInfoJson) && File.Exists(deeperGameFilePath))
					{
						BaseInfoContentModel.Instance.SetValue("fileInfoJson", deeperFileInfoJson);
						BaseInfoContentModel.Instance.SetValue("version", version);
						result = true;
					}
				}
			}
			
			return result;
		}

		public static string CheckExistVersion()
		{
			Version maxVersion = null;

			var defaultPath = Path.Combine(AppDomain.CurrentDomain.BaseDirectory, "Game");
			var targetPath = BaseInfoContentModel.Instance.GetValue<string>("gameInstallPath", defaultPath);

			if (!targetPath.EndsWith("IntoTheVoid"))
			{
				targetPath = Path.Combine(targetPath, "IntoTheVoid");
			}

			if (Directory.Exists(targetPath))
			{
				var files = Directory.GetFiles(targetPath, "*_info.json", System.IO.SearchOption.TopDirectoryOnly);

				foreach (var file in files)
				{
					string fileName = Path.GetFileName(file);
					if (fileName.EndsWith("_info.json"))
					{
						string versionStr = fileName.Substring(0, fileName.Length - "_info.json".Length);
						if (Version.TryParse(versionStr, out Version currentVersion))
						{
							if (maxVersion == null || currentVersion > maxVersion)
							{
								maxVersion = currentVersion;
							}
						}
					}
				}
			}

			return maxVersion?.ToString() ?? "";
		}

		public static void ClearAllFiles()
		{
			var basePath = AppDomain.CurrentDomain.BaseDirectory;
			var defaultPath = System.IO.Path.Combine(basePath, "Game");
			var targetPath = defaultPath;
			try
			{
				targetPath = BaseInfoContentModel.Instance.GetValue<string>("gameInstallPath", defaultPath);

				if (Directory.Exists(targetPath))
				{
					Directory.Delete(targetPath, true);
				}
			}
			catch (Exception ex)
			{
				LogHelper.Error(ex, "ClearGameFileError");
			}

			try
			{
				BaseInfoContentModel.Instance.DeleteFile();
			}
			catch (Exception ex)
			{
				LogHelper.Error(ex, "BaseInfoContentModel.Instance.DeleteFile");
			}

			Common.SaveInstallPath(targetPath);
		}
		public static void ClearDownloadFile()
		{
			var basePath = AppDomain.CurrentDomain.BaseDirectory;
			var defaultPath = System.IO.Path.Combine(basePath, "Game");
			var targetPath = defaultPath;
			try
			{
				targetPath = BaseInfoContentModel.Instance.GetValue<string>("gameInstallPath", defaultPath);

				if (Directory.Exists(targetPath))
				{

					var filesToDelete = Directory.GetFiles(targetPath, "*.zip")
						.Concat(Directory.GetFiles(targetPath, "*.temp"));

					foreach (var file in filesToDelete)
					{
						File.Delete(file);
					}
					// 不删除文件夹只删除下载的文件
					// Directory.Delete(targetPath, true);
				}

				// BaseInfoContentModel.Instance.DeleteFile();
			}
			catch (Exception ex)
			{
				LogHelper.Error(ex, "ClearGameFileError");
			}
			Common.SaveInstallPath(targetPath);
		}
		public static void SaveInstallPath(string path)
		{
			BaseInfoContentModel.Instance.SetValue("gameInstallPath", path);
			SeveGameInstallPathInReg(path);
		}
		// 这里的注册表数值要与product中一样
		static string RegKeyPath = @"SOFTWARE\Microsoft\Jinzhangshu\IntoTheVoid";
		static private void SeveGameInstallPathInReg(string path)
		{
			try
			{
				if (!path.EndsWith("\\"))
					path += "\\";
				RegistryKey pathKey = Registry.CurrentUser.OpenSubKey(RegKeyPath, true);
				if (pathKey == null)
				{
					var key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft", true);
					pathKey = key.CreateSubKey("Jinzhangshu", true);
					pathKey = pathKey.CreateSubKey("IntoTheVoid", true);
					key.Close();
				}
				pathKey.SetValue("GameInstallDir", path);
				// SaveTapReg(path);
			}
			catch (Exception e)
			{
				LogHelper.Error("White Reg Error! Exception:" + e);
			}
		}
		static public void SaveTapReg()
		{
			var basePath = AppDomain.CurrentDomain.BaseDirectory;
			var defaultPath = System.IO.Path.Combine(basePath, "Game");
			string path = BaseInfoContentModel.Instance.GetValue<string>("gameInstallPath", defaultPath);
			try
			{
				RegistryKey pathKey = Registry.CurrentUser.OpenSubKey(RegKeyPath, true);
				if (pathKey == null)
				{
					var key = Registry.CurrentUser.OpenSubKey("SOFTWARE\\Microsoft", true);
					pathKey = key.CreateSubKey("Jinzhangshu", true);
					pathKey = pathKey.CreateSubKey("IntoTheVoid", true);
					key.Close();
				}
				var exePath = System.IO.Path.Combine(path, "IntoTheVoid\\IntoTheVoid.exe");
				pathKey.SetValue("URL Protocol", exePath);
				pathKey = pathKey.CreateSubKey("Shell\\Open\\Command", true);
				pathKey.SetValue("", '"' + exePath + '"');

				pathKey = Registry.CurrentUser.OpenSubKey(RegKeyPath, true);
				pathKey = pathKey.CreateSubKey("DefaultIcon");
				pathKey.SetValue("", "IntoTheVoid.exe");
			}
			catch (Exception e)
			{
				LogHelper.Error("White Reg Error! Exception:" + e);
			}
		}
		static public void CheckGameIsExist()
		{
			var install = GetGameInstallPathReg();
			if (install == null)
				return;

			var exePath = System.IO.Path.Combine(install, "IntoTheVoid\\IntoTheVoid.exe");
			if (install != null && File.Exists(exePath))
			{
				SaveTapReg();
			}
		}
		static public string GetGameInstallPathReg()
		{
			try
			{
				using (RegistryKey pathKey = Registry.CurrentUser.OpenSubKey(RegKeyPath))
				{
					if (pathKey == null)
						return null;
					var strs = pathKey.GetValue("GameInstallDir");
					if (strs == null)
						return null;
					return strs.ToString();
				}
			}
			catch { }
			return null;
		}
		public static bool CheckLauncherVersion(string version)
		{
			if (version == null || version == "")
			{
				return true;
			}

			var currentVersion = System.Reflection.Assembly.GetExecutingAssembly().GetName().Version.ToString();

			var v1 = currentVersion.Split('.');
			var v2 = version.Split('.');
			int[] current = new int[4] { 0, 0, 0, 0 };
			int[] online = new int[4] { 0, 0, 0, 0 };
			for (int i = 3; i >= 0; --i)
			{
				if (v1.Length <= i)
				{
					current[i] = 0;
				}
				else
				{
					current[i] = int.Parse(v1[i]);
				}

				if (v2.Length <= i)
				{
					online[i] = 0;
				}
				else
				{
					online[i] = int.Parse(v2[i]);
				}
			}
			for (int i = 0; i < 4; ++i)
			{
				if (current[i] < online[i])
				{
					return false;
				}
				if (current[i] > online[i])
				{
					return true;
				}
			}
			return true;
		}

		public static string CaulculateFileMd5(String filePath)
		{
			string md5 = null;
			if (filePath == null || !File.Exists(filePath))
			{
				return md5;
			}

			using (FileStream fs = File.OpenRead(filePath))
			{
				using (var crypto = MD5.Create())
				{
					var md5Hash = crypto.ComputeHash(fs);
					md5 = md5Hash.Aggregate(string.Empty, (res, b) => res = res + b.ToString("X2"));
				}
			}
			return md5;
		}
	}
}
